package com.m1.tzolkin.model.temples;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashSet;
import java.util.Set;

import com.m1.tzolkin.model.Action;
import com.m1.tzolkin.model.Season;
import com.m1.tzolkin.model.TzolkinConstants;
import com.m1.tzolkin.model.board.ITZBoard;
import com.m1.tzolkin.model.exceptions.StepMoveException;
import com.m1.tzolkin.model.players.ITZPlayer;
import com.m1.tzolkin.model.util.zone.IStepsZone;
import com.m1.tzolkin.model.util.zone.StepsZonesManager;

public class TemplesManager extends StepsZonesManager<ITemple, ITZPlayer>
							implements ITemplesManager, TzolkinConstants {
	
	// ATTRIBUTS
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 2270102872477482877L;
	private int calledTime;
	private final PropertyChangeSupport listeners;
	
	// CONSTRUCTEUR
	
	public TemplesManager() {
		super(TempleEnum.values());
		calledTime = 0;
		listeners = new PropertyChangeSupport(this);
	}
	
	// REQUETES
	
	@Override
	public PropertyChangeListener[] getPropertyChangeListeners() {
		return listeners.getPropertyChangeListeners();
	}
	
	@Override
	public boolean canUp(ITemple t, ITZPlayer p) {
		if (!super.canUp(t, p)) {
			return false;
		}
		IStepsZone<ITZPlayer> zone = getStepsZone(t);
		for (ITZPlayer pl : zone.getKeys()) {
			if (pl != p && zone.getStep(pl) == zone.getStepsNb() - 1) {
				return false;
			}
		}
		return true;
	}
	
	// COMMANDES
	
	@Override
	public void addPropertyChangeListener(PropertyChangeListener l) {
		listeners.addPropertyChangeListener(l);
	}
	
	@Override
	public void removePropertyChangeListener(PropertyChangeListener l) {
		listeners.removePropertyChangeListener(l);
	}

	@Override
	public void activeTemples(ITZBoard b, Season s) {
		Action[] a;
		// Peut donner des cr�nes ?
		final int SKULL_POS = 5;
		final Season SKULL_SEASON = Season.MIDDLE_ERA;
		boolean canGive = true;
		if (s.equals(SKULL_SEASON)) {
			int skulls = 0;
			for (ITZPlayer p : getStepsZone(TempleEnum.KUKULCAN).getKeys()) {
				if (getPosition(TempleEnum.KUKULCAN, p) > SKULL_POS) {
					skulls++;
				}
			}
			canGive = b.getBank().getSkulls() >= skulls;
		}
		// Activation
		boolean give;
		for (ITemple t : getZones()) {
			give = canGive || t.equals(TempleEnum.KUKULCAN);
			a = t.getActions(s);
			Set<ITZPlayer> pl = shareTop(t);
			boolean share = pl.size() <= 1;
			for (ITZPlayer p : getStepsZone(t).getKeys()) {
				int pos = getPosition(t, p);
				if (a[pos] != null) {
					try {
						if (pos != SKULL_POS || give) {
							a[pos].doAction(b, p);
						}
					} catch (Exception e) { /* Rien */ }
				}
				if (s.templesBelowActions()) {
					for (int i = pos - 1; i >= DEFAULT_TEMPLE_MARCH; --i) {
						if (a[i] != null) {
							try {
								if (i != SKULL_POS || give) {
									a[i].doAction(b, p);
								}
							} catch (Exception e) { /* Rien */ }
						}
					}
				}
				if (s.templesTopPoints() && pl.contains(p)) {
					if (share) {
						p.addScore((int) (t.getTopPoints(calledTime) / 2));
					} else {
						p.addScore(t.getTopPoints(calledTime));
					}
				}
			}
		}
		if (s.templesTopPoints()) {
			++calledTime;
		}
	}
	
	@Override
	public void up(ITemple t, ITZPlayer p) throws StepMoveException {
		IStepsZone<ITZPlayer> zone = getStepsZone(t);
		for (ITZPlayer pl : zone.getKeys()) {
			if (pl != p && zone.getStep(pl) == zone.getStepsNb() - 1) {
				throw new StepMoveException();
			}
		}
		super.up(t, p);
		if (zone.getStep(p) == zone.getStepsNb() - 1) {
			p.setTurnTwice(true);
		}
		listeners.firePropertyChange("upTemple", null, null);
	}
	
	@Override
	public void down(ITemple t, ITZPlayer p) throws StepMoveException {
		super.down(t, p);
		listeners.firePropertyChange("downTemple", null, null);
	}
	
	// OUTILS
	
	/**
	 * Retourne l'ensemble des joueurs sur la marche la plus haute de t.
	 */
	private Set<ITZPlayer> shareTop(ITemple t) {
		IStepsZone<ITZPlayer> zone = getStepsZone(t);
		int highest = 0;
		// Calcul de la plus haute marche occup�e
		for (ITZPlayer p : zone.getKeys()) {
			highest = Math.max(highest, zone.getStep(p));
		}
		// R�union des joueurs � cette marche
		Set<ITZPlayer> pl = new HashSet<ITZPlayer>();
		for (ITZPlayer p : zone.getKeys()) {
			if (zone.getStep(p) == highest) {
				pl.add(p);
			}
		}
		return pl;
	}

}
